home *** CD-ROM | disk | FTP | other *** search
/ Java Developer's Companion / Java Developer's Companion.iso / Javacup / PR8ADPL7.TAR / productivity_tools / PR8ADPL7 / ImageEditorComponent.java < prev    next >
Encoding:
Java Source  |  1996-05-23  |  21.6 KB  |  834 lines

  1. // ImageEditorComponent.java
  2. // A painting program able to load and save images via JFS
  3. import java.awt.*;
  4. import java.awt.image.ImageProducer;
  5. import java.awt.image.CropImageFilter;
  6. import java.awt.image.FilteredImageSource;
  7. import java.awt.image.PixelGrabber;
  8. import java.awt.image.MemoryImageSource;
  9. import java.util.Vector;
  10.  
  11. public class ImageEditorComponent extends JFScomponent
  12. {
  13.     Image image;                // last loaded image
  14.     ImageButton loadb, saveb, clearb,    // file control buttons
  15.             undob, printb, saveselb;
  16.     ImageToggle freehandb, lineb, boxb,    // drawing mode buttons
  17.             circleb, brushb, handb,
  18.             polygonb, floodfillb, textb,
  19.             selectb;
  20.     ImageButton fontb, prefsb;        // prefs buttons
  21.     ImageToggle emptyb, filledb;        // solid/empty buttons
  22.     ImageToggle sizeb[]=new ImageToggle[6];    // brush size buttons
  23.     ColourCanvas col;            // current colour display
  24.     ColourSlider red,green,blue;        // sliders for colour
  25.     PaintingCanvas pc;            // painting area
  26.     BorderPanel mid;            // panel canvas is in
  27.     FileReq loadreq, savereq;        // load and save requestors
  28.  
  29.     String mtype[] = {"image/ppm", "image/gif", "image/jpeg"};
  30.  
  31.     void init(java.applet.Applet a)
  32.     {
  33.     setLayout(new BorderLayout());
  34.  
  35.     // Create left button panel
  36.     Panel left = new Panel();
  37.     left.setLayout(new GridLayout(1,2));
  38.  
  39.     // Create left left column of buttons
  40.     ControlPanel lleft = new ControlPanel(a);
  41.     loadb = lleft.addbutton("images/load.gif");
  42.     saveb = lleft.addbutton("images/save.gif");
  43.     saveselb = lleft.addbutton("images/savesel.gif");
  44.     lleft.addgap(7);
  45.     ImageToggleGroup modeg = new ImageToggleGroup();
  46.     handb = lleft.addtoggle("images/hand.gif", modeg, false);
  47.     freehandb = lleft.addtoggle("images/freehand.gif", modeg, true);
  48.     lineb = lleft.addtoggle("images/line.gif", modeg, false);
  49.     boxb = lleft.addtoggle("images/box.gif", modeg, false);
  50.     circleb = lleft.addtoggle("images/circle.gif", modeg, false);
  51.     lleft.addgap(10);
  52.     fontb = lleft.addbutton("images/font.gif");
  53.     left.add(lleft);
  54.  
  55.     // Create right left column of buttons
  56.     ControlPanel rleft = new ControlPanel(a);
  57.     printb = rleft.addbutton("images/print.gif");
  58.     clearb = rleft.addbutton("images/clear.gif");
  59.     undob = rleft.addbutton("images/undo.gif");
  60.     rleft.addgap(7);
  61.     brushb = rleft.addtoggle("images/brush.gif", modeg, false);
  62.     polygonb = rleft.addtoggle("images/polygon.gif", modeg, false);
  63.     floodfillb = rleft.addtoggle("images/floodfill.gif", modeg, false);
  64.     textb = rleft.addtoggle("images/text.gif", modeg, false);
  65.     selectb = rleft.addtoggle("images/select.gif", modeg, false);
  66.     left.add(rleft);
  67.     rleft.addgap(10);
  68.     prefsb = rleft.addbutton("images/prefs.gif");
  69.     add("West",left);
  70.  
  71.     // Create right button panel
  72.     ControlPanel right = new ControlPanel(a);
  73.     ImageToggleGroup fillg = new ImageToggleGroup();
  74.     emptyb = right.addtoggle("images/empty.gif", fillg, true);
  75.     filledb = right.addtoggle("images/filled.gif", fillg, false);
  76.     right.addgap(7);
  77.     ImageToggleGroup sizeg = new ImageToggleGroup();
  78.     sizeb[0] = right.addtoggle("images/size1.gif", sizeg, true);
  79.     sizeb[1] = right.addtoggle("images/size2.gif", sizeg, false);
  80.     sizeb[2] = right.addtoggle("images/size3.gif", sizeg, false);
  81.     sizeb[3] = right.addtoggle("images/size4.gif", sizeg, false);
  82.     sizeb[4] = right.addtoggle("images/size5.gif", sizeg, false);
  83.     sizeb[5] = right.addtoggle("images/size6.gif", sizeg, false);
  84.     add("East",right);
  85.  
  86.     // Create bottom colour choosers
  87.     Panel bot = new Panel();
  88.     bot.setLayout(new BorderLayout());
  89.     bot.add("West",col = new ColourCanvas());
  90.     SpacedPanel sliders = new SpacedPanel(2,0);
  91.     sliders.setLayout(new GridLayout(3,1));
  92.     sliders.add(red = new ColourSlider('r'));
  93.     sliders.add(green = new ColourSlider('g'));
  94.     sliders.add(blue = new ColourSlider('b'));
  95.     bot.add("Center", sliders);
  96.     add("South",bot);
  97.  
  98.     // create main painting area
  99.     mid = new BorderPanel(Color.black, new Color(230,230,230));
  100.     mid.setLayout(new GridLayout(1,1));
  101.     mid.add("Center",pc = new PaintingCanvas(this));
  102.     add("Center",mid);
  103.     }
  104.  
  105.     public void connect(JFSclient c)
  106.     {
  107.     super.connect(c);
  108.     try if (!client.canaccess("/dev/Printer", 'w')) printb.disable();
  109.     catch(RequestException e);
  110.     }
  111.  
  112.     // load
  113.     // Load the given file, and wait while it is converted from PPM format
  114.     void load(String file, int ver) throws RequestException
  115.     {
  116.     byte fd[] = client.get(file, ver);
  117.     BufferInputStream buf = new BufferInputStream(fd);
  118.     Image i = createImage(new PPMloader(buf));
  119.     MediaTracker waiter = new MediaTracker(this);
  120.     waiter.addImage(i, 666);
  121.     try waiter.waitForAll();
  122.     catch(InterruptedException e);
  123.     if (waiter.isErrorAny()) new ErrorWindow("Error loading image");
  124.     else {
  125.         if (pc != null) pc.setimage(i);
  126.         else image = i;
  127.         }
  128.     client.setcurrent(file);
  129.     }
  130.  
  131.     Dimension wantedsize()
  132.     {
  133.     return new Dimension(600,400);
  134.     }
  135.  
  136.     void print(String printer, String type) throws RequestException
  137.     {
  138.     if (!type.equals("PPM"))
  139.         throw new RequestException("Only PPM printers are supported");
  140.     BufferOutputStream buf = new BufferOutputStream();
  141.     ImageProducer src;
  142.     if (pc != null) src = pc.getimage().getSource();
  143.     else src = image.getSource();
  144.     PPMsaver sv = new PPMsaver();
  145.     src.startProduction(sv);
  146.     sv.output(buf, true);
  147.     src.removeConsumer(sv);
  148.     Message pmsg = new Message();
  149.     pmsg.add("File","/dev/Printer");
  150.     pmsg.add("Printer",printer);
  151.     pmsg.setdata(buf.getarray());
  152.     try client.send("Put", pmsg);
  153.     catch(RequestException e)
  154.         throw new RequestException("Error writing to printer");
  155.     }
  156.  
  157.     // action
  158.     // Handle a user command
  159.     public boolean action(Event e, Object o)
  160.     {
  161.     // File button set
  162.     if (e.target == loadb && loadreq == null) {
  163.         loadreq = new FileReq(client, this, "Load", "image/*",
  164.                       false, null);
  165.         }
  166.     else if (e.target == saveb && pc.getimage() != null) {
  167.         savereq = new FileReq(client, this, "Save", "image/*",
  168.                       true, mtype);
  169.         }
  170.     else if (e.target == clearb)
  171.         new ImageSizeReq(pc);
  172.     else if (e.target == undob)
  173.         pc.undo();
  174.     else if (e.target == printb)
  175.         new PrintRequestor(this, client);
  176.  
  177.     // Drawing mode button set
  178.     else if (e.target == freehandb)
  179.         pc.setmode(PaintingCanvas.FREEHAND);
  180.     else if (e.target == lineb)
  181.         pc.setmode(PaintingCanvas.LINE);
  182.     else if (e.target == boxb)
  183.         pc.setmode(PaintingCanvas.BOX);
  184.     else if (e.target == circleb)
  185.         pc.setmode(PaintingCanvas.CIRCLE);
  186.     else if (e.target == brushb)
  187.         pc.setmode(PaintingCanvas.BRUSH);
  188.     else if (e.target == handb)
  189.         pc.setmode(PaintingCanvas.HAND);
  190.     else if (e.target == polygonb)
  191.         pc.setmode(PaintingCanvas.POLYGON);
  192.     else if (e.target == textb)
  193.         pc.setmode(PaintingCanvas.TEXT);
  194.     else if (e.target == selectb)
  195.         pc.setmode(PaintingCanvas.SELECT);
  196.     else if (e.target == floodfillb)
  197.         pc.setmode(PaintingCanvas.FLOODFILL);
  198.  
  199.     // Prefs button set
  200.     else if (e.target == fontb) {
  201.         new FontRequestor(this, pc.getfont());
  202.         }
  203.     else if (e.target == prefsb) {
  204.         }
  205.  
  206.     // Colour controls
  207.     if (e.target == red || e.target == green || e.target == blue) {
  208.         // change the current drawing colour
  209.         Color nc = new Color(red.getvalue(),
  210.                      green.getvalue(),
  211.                      blue.getvalue());
  212.         col.set(nc);
  213.         if (pc != null) pc.setcolour(nc);
  214.         }
  215.  
  216.     // File requestors
  217.     else if (e.target == loadreq) {
  218.         if (((String)e.arg).equals("Load")) {
  219.             // load a file
  220.             try load(loadreq.getfile(), loadreq.getversion());
  221.             catch(RequestException ex)
  222.                 new ErrorWindow("Could not open "+
  223.                         loadreq.getfile()+" : "+
  224.                         ex.getMessage());
  225.             }
  226.         loadreq = null;
  227.         }
  228.     else if (e.target == savereq) {
  229.         if (((String)e.arg).equals("Save")) {
  230.             // Write image data to an array in the correct format
  231.             String type = savereq.gettype();
  232.             BufferOutputStream buf = new BufferOutputStream();
  233.             ImageProducer src = pc.getimage().getSource();
  234.             if (type.equals("image/ppm")) {
  235.                 PPMsaver sv = new PPMsaver();
  236.                 src.startProduction(sv);
  237.                 sv.output(buf, true);
  238.                 src.removeConsumer(sv);
  239.                 }
  240.             else {
  241.                 new ErrorWindow("Cannot save format "+type);
  242.                 return false;
  243.                 }
  244.  
  245.             // Write out the data
  246.             try save(buf.getarray(), savereq.getfile(), type,
  247.                  savereq.getversion(), savereq.multiversion());
  248.             catch(RequestException ex)
  249.                 new ErrorWindow("Could not save "+
  250.                         savereq.getfile()+" : "+
  251.                         ex.getMessage());
  252.             }
  253.         savereq = null;
  254.         }
  255.  
  256.     // Font chosen
  257.     else if (e.target instanceof FontRequestor)
  258.         pc.setfont(((FontRequestor)e.target).getfont());
  259.  
  260.     // Fill type button set
  261.     else if (e.target == filledb)
  262.         pc.setfillmode(PaintingCanvas.FILLED);
  263.     else if (e.target == emptyb)
  264.         pc.setfillmode(PaintingCanvas.EMPTY);
  265.  
  266.     // Brush size button set
  267.     else {
  268.         for(int i=0; i<sizeb.length; i++)
  269.             if (e.target == sizeb[i])
  270.                 pc.setbrushsize(i+1);
  271.         }
  272.  
  273.     return true;
  274.     }
  275. }
  276.  
  277. // ColourCanvas
  278. // A box displaying a single solid colour.
  279. class ColourCanvas extends Canvas
  280. {
  281.     int red,green,blue;
  282.     int border = 3;
  283.     Color col = Color.black;
  284.     Color hi = new Color(230,230,230), lo = new Color(50,50,50);
  285.  
  286.     // set
  287.     // Set the colour being displayed
  288.     void set(Color c)
  289.     {
  290.     col = c;
  291.     paint(getGraphics());
  292.     }
  293.  
  294.     public void paint(Graphics g)
  295.     {
  296.     int w = size().width, h = size().height;
  297.     g.setColor(col);
  298.     g.fillRect(0, 0, w, h);
  299.     g.setColor(hi);
  300.     for(int i=0; i<border; i++) {
  301.         g.drawLine(i,i,w-i,i);
  302.         g.drawLine(i,i,i,h-i);
  303.         }
  304.     g.setColor(lo);
  305.     for(int i=0; i<border; i++) {
  306.         g.drawLine(w-i,h-i, w-i,i);
  307.         g.drawLine(w-i,h-i, i,h-i);
  308.         }
  309.     }
  310.  
  311.     public Dimension minimumSize()
  312.     {
  313.     return new Dimension(51,51);
  314.     }
  315.  
  316.     public Dimension preferredSize()
  317.     {
  318.     return minimumSize();
  319.     }
  320. }
  321.  
  322. // ColourSlider
  323. // A slider for choosing a red, green or blue value
  324. class ColourSlider extends Canvas
  325. {
  326.     char primary;
  327.     Image im;        // image containing a smooth colour range
  328.     int w,h;        // size of this component
  329.     int pos = 0;        // slider position (0-255)
  330.  
  331.     // Construct a new slider for the given primary
  332.     ColourSlider(char p) { primary = p; }
  333.  
  334.     // Return the current colour value, as a number from 0-255
  335.     int getvalue() { return pos; }
  336.  
  337.     public void reshape(int nx, int ny, int nw, int nh)
  338.     {
  339.     if (nw <= 2 || nh <= 2)
  340.         invalidate();
  341.     else if (nw != w || nh != h) {
  342.         w = nw; h = nh;
  343.         im = createImage(w-2,h-2);
  344.         Graphics g = im.getGraphics();
  345.         int val = -1;
  346.         for(int i=0; i<w-2; i++) {
  347.             if (i*256/w != val) {
  348.                 val = i*256/w;
  349.                 g.setColor(new Color(primary=='r'?val:0,
  350.                              primary=='g'?val:0,
  351.                              primary=='b'?val:0));
  352.                 }
  353.             g.drawLine(i, 1, i, h-1);
  354.             }
  355.         }
  356.     super.reshape(nx, ny, nw, nh);
  357.     }
  358.  
  359.     public boolean mouseDown(Event e, int x, int y)
  360.     {
  361.     return mouseDrag(e, x, y);
  362.     }
  363.  
  364.     public boolean mouseDrag(Event e, int x, int y)
  365.     {
  366.     pos = Math.min(Math.max(x*256/w,0),255);
  367.     paint(getGraphics());
  368.     getParent().postEvent(new Event(this, Event.ACTION_EVENT, null));
  369.     return true;
  370.     }
  371.  
  372.     // paint
  373.     // Render this slider
  374.     public void paint(Graphics g)
  375.     {
  376.     int x = pos*w/256;
  377.     g.drawImage(im, 1, 1, this);
  378.     g.setColor(Color.white);
  379.     g.fillRect(x-2, 1, 5, h-2);
  380.     }
  381.  
  382.     public Dimension minimumSize()
  383.     {
  384.     return new Dimension(50,10);
  385.     }
  386.  
  387.     public Dimension preferredSize()
  388.     {
  389.     return minimumSize();
  390.     }
  391. }
  392.  
  393. // PaintingCanvas
  394. // A component for doing drawing on
  395. class PaintingCanvas extends Canvas
  396. {
  397.     ImageEditorComponent parent;
  398.     Image bogusim;        // an unusable image waiting to be converted
  399.     Image im;        // image being edited, or null
  400.     Graphics img;        // graphics of im
  401.     Image uim;        // undo image
  402.     int xoff,yoff;        // offset of image from corner
  403.     int xhand, yhand;    // where the hand tool started dragging
  404.     int mode = FREEHAND;
  405.     int brushsz = 1;
  406.     int fillmode = EMPTY;
  407.     Color col = Color.black;
  408.     Polygon poly = null;    // polygon being drawn
  409.     Font fn = new Font("TimesRoman", Font.PLAIN, 10);
  410.     static final int FREEHAND = 0;    // draw modes
  411.     static final int LINE = 1;
  412.     static final int BOX = 2;
  413.     static final int CIRCLE = 3;
  414.     static final int BRUSH = 4;
  415.     static final int HAND = 5;
  416.     static final int POLYGON = 6;
  417.     static final int TEXT = 7;
  418.     static final int SELECT = 8;
  419.     static final int FLOODFILL = 9;
  420.     static final int FILLED = 0;    // fill modes for solid shapes
  421.     static final int EMPTY = 1;
  422.  
  423.     PaintingCanvas(ImageEditorComponent p)
  424.     {
  425.     parent = p;
  426.     }
  427.  
  428.     // setimage
  429.     // Set the image currently being edited
  430.     void setimage(Image i)
  431.     {
  432.     bogusim = i;
  433.     if (isShowing()) convert();
  434.     }
  435.  
  436.     // setmode
  437.     // Set the current drawing mode. Must be one of the ones listed above
  438.     void setmode(int m) { mode = m; poly = null; }
  439.  
  440.     // setcolour
  441.     // Set the current drawing colour
  442.     void setcolour(Color c) { col = c; }
  443.  
  444.     // setbrushsize
  445.     // Set the current brush size
  446.     void setbrushsize(int b) { brushsz = b; }
  447.  
  448.     // setfillmode
  449.     // Set the mode used for filling boxes, ovals and polygons
  450.     void setfillmode(int m) { fillmode = m; }
  451.  
  452.     // setfont
  453.     // Set the font for text drawing
  454.     void setfont(Font f) { fn = f; }
  455.  
  456.     // getfont
  457.     // Gets the currently set font
  458.     Font getfont() { return fn; }
  459.  
  460.     // undo
  461.     // Copy the undo image to the current image, and blit it to the front
  462.     void undo()
  463.     {
  464.     if (img == null) return;
  465.     img.drawImage(uim, 0, 0, this);
  466.     blit();
  467.     }
  468.  
  469.     // getimage
  470.     // Returns the image currently being edited
  471.     Image getimage()
  472.     {
  473.     if (im == null && bogusim != null)
  474.         return bogusim;
  475.     return im;
  476.     }
  477.  
  478.     // convert
  479.     // Copy the image in bogusim to another image so it can be actually
  480.     // used. This can only be called when the component is showing.
  481.     void convert()
  482.     {
  483.     im = createImage(bogusim.getWidth(this), bogusim.getHeight(this));
  484.     uim = createImage(bogusim.getWidth(this), bogusim.getHeight(this));
  485.     img = im.getGraphics();
  486.     img.drawImage(bogusim, 0, 0, this);
  487.     uim.getGraphics().drawImage(bogusim, 0, 0, this);
  488.     bogusim = null;
  489.     paint(getGraphics());
  490.     }
  491.  
  492.     // blit
  493.     // Copy the current image to the front (assumes there IS a current
  494.     // image)
  495.     void blit()
  496.     {
  497.     getGraphics().drawImage(im, xoff, yoff, this);
  498.     }
  499.  
  500.     // backup
  501.     // Copy the current image to the undo buffer
  502.     void backup()
  503.     {
  504.     uim.getGraphics().drawImage(im, 0, 0, this);
  505.     }
  506.  
  507.     // paint
  508.     // Clear the front, draw the image and surround it with a nice border
  509.     public void paint(Graphics g)
  510.     {
  511.     if (bogusim != null) convert();
  512.     g.setColor(Color.lightGray);
  513.     g.fillRect(0, 0, size().width, size().height);
  514.     if (im == null) return;        // no image
  515.     blit();
  516.     g.setColor(Color.black);
  517.     g.drawRect(xoff-1, yoff-1,
  518.            im.getWidth(this)+1, im.getHeight(this)+1);
  519.     }
  520.  
  521.     int lx,ly;        // last click point
  522.  
  523.     public boolean mouseDown(Event e, int sx, int sy)
  524.     {
  525.     if (im == null) return false;
  526.     lx = Math.min(Math.max(sx-xoff,0),im.getWidth(this));
  527.     ly = Math.min(Math.max(sy-yoff,0),im.getHeight(this));
  528.     if (mode == POLYGON) {
  529.         if (poly == null) {
  530.             backup();    // 1st point
  531.             poly = new Polygon();
  532.             poly.addPoint(lx, ly);
  533.             }
  534.         if (poly.npoints >= 3 &&
  535.             Math.abs(lx - poly.xpoints[poly.npoints-2]) < 3 &&
  536.             Math.abs(ly - poly.ypoints[poly.npoints-2]) < 3) {
  537.             // last point clicked
  538.             poly = null;
  539.             }
  540.         else poly.addPoint(lx, ly);
  541.         }
  542.     else if (mode == HAND) {
  543.         xhand = lx;
  544.         yhand = ly;
  545.         }
  546.     else
  547.         backup();
  548.     requestFocus();        // so we get chars in TEXT mode
  549.     return true;
  550.     }
  551.  
  552.     public boolean mouseDrag(Event e, int sx, int sy)
  553.     {
  554.     if (im == null) return false;
  555.     int rx = Math.min(Math.max(sx-xoff,0),im.getWidth(this));
  556.     int ry = Math.min(Math.max(sy-yoff,0),im.getHeight(this));
  557.     img.setColor(col);
  558.     if (mode == FREEHAND) {
  559.         img.drawLine(lx, ly, rx, ry);
  560.         blit();
  561.         lx = rx; ly = ry;
  562.         }
  563.     else if (mode == LINE) {
  564.         undo();
  565.         img.drawLine(lx, ly, rx, ry);
  566.         blit();
  567.         }
  568.     else if (mode == BOX) {
  569.         undo();
  570.         int x = Math.min(lx,rx), y = Math.min(ly,ry);
  571.         int w = Math.abs(lx-rx), h = Math.abs(ly-ry);
  572.         if (fillmode == EMPTY) img.drawRect(x, y, w, h);
  573.         else img.fillRect(x, y, w, h);
  574.         blit();
  575.         }
  576.     else if (mode == CIRCLE) {
  577.         undo();
  578.         int w = Math.abs(lx-rx), h = Math.abs(ly-ry);
  579.         if (fillmode == EMPTY) img.drawOval(lx-w, ly-h, w*2, h*2);
  580.         else img.fillOval(lx-w, ly-h, w*2, h*2);
  581.         blit();
  582.         }
  583.     else if (mode == BRUSH) {
  584.         img.fillOval(rx-brushsz, ry-brushsz, brushsz*2, brushsz*2);
  585.         blit();
  586.         }
  587.     else if (mode == HAND) {
  588.         if (im.getWidth(this) > size().width) {
  589.             xoff = sx - xhand;
  590.             if (xoff > 0)
  591.                 xoff = 0;
  592.             else if (xoff < size().width-im.getWidth(this))
  593.                 xoff = size().width-im.getWidth(this);
  594.             }
  595.         if (im.getHeight(this) > size().height) {
  596.             yoff = sy - yhand;
  597.             if (yoff > 0)
  598.                 yoff = 0;
  599.             else if (yoff < size().height-im.getHeight(this))
  600.                 yoff = size().height-im.getHeight(this);
  601.             }
  602.         blit();
  603.         }
  604.     else if (mode == SELECT) {
  605.         undo();
  606.         int x = Math.min(lx,rx), y = Math.min(ly,ry);
  607.         int w = Math.abs(lx-rx), h = Math.abs(ly-ry);
  608.         img.drawRect(x, y, w, h);
  609.         blit();
  610.         }
  611.     return true;
  612.     }
  613.  
  614.     public boolean mouseMove(Event e, int sx, int sy)
  615.     {
  616.     if (mode == POLYGON && poly != null) {
  617.         int rx = Math.min(Math.max(sx-xoff,0),im.getWidth(this));
  618.         int ry = Math.min(Math.max(sy-yoff,0),im.getHeight(this));
  619.         undo();
  620.         img.setColor(col);
  621.         poly.xpoints[poly.npoints-1] = rx;
  622.         poly.ypoints[poly.npoints-1] = ry;
  623.         if (fillmode == EMPTY || poly.npoints < 3)
  624.             img.drawPolygon(poly);
  625.         else
  626.             img.fillPolygon(poly);
  627.         blit();
  628.         }
  629.     return true;
  630.     }
  631.  
  632.     public boolean mouseUp(Event e, int sx, int sy)
  633.     {
  634.     if (im == null) return false;
  635.     int rx = Math.min(Math.max(sx-xoff,0),im.getWidth(this));
  636.     int ry = Math.min(Math.max(sy-yoff,0),im.getHeight(this));
  637.     if (mode == SELECT) {
  638.         // when in select mode, dragging out an area copies to
  639.         // the clipboard, and clicking at some point pastes.
  640.         if (Math.abs(lx - rx) < 3 && Math.abs(ly - ry) < 3) {
  641.             // Paste if we can
  642.             Object cbuf = parent.client.getbuf();
  643.             if (cbuf != null && cbuf instanceof Image) {
  644.                 img.drawImage((Image)cbuf, rx, ry, this);
  645.                 blit();
  646.                 }
  647.             }
  648.         else {
  649.             // Copy selected area
  650.             undo();
  651.             int x = Math.min(lx,rx), y = Math.min(ly,ry);
  652.             int w = Math.abs(lx-rx), h = Math.abs(ly-ry);
  653.             Image cut = createImage(
  654.                      new FilteredImageSource(im.getSource(),
  655.                       new CropImageFilter(x, y, w, h)));
  656.             parent.client.setbuf(cut);
  657.             }
  658.         }
  659.     else if (mode == FLOODFILL) {
  660.         // Get image pixels
  661.         int w = im.getWidth(this), h = im.getHeight(this);
  662.         int arr[] = new int[w * h];
  663.         PixelGrabber grab = new PixelGrabber(im, 0, 0, w, h, arr, 0, w);
  664.         try grab.grabPixels(1);
  665.         catch(InterruptedException ex) {
  666.             return true;
  667.             }
  668.  
  669.         // Flood from seed point, staying within seed colour
  670.         int c = (255<<24) + (col.getRed()<<16) +
  671.             (col.getGreen()<<8) + col.getBlue();
  672.         int p = arr[ry*w + rx];
  673.         if (p != c) flood(arr, w, h, rx, ry, p, c);
  674.  
  675.         // Convert pixels back to an image
  676.         Image back = createImage(
  677.                   new MemoryImageSource(w, h, arr, 0, w));
  678.         img.drawImage(back, 0, 0, this);
  679.         blit();
  680.         }
  681.     return true;
  682.     }
  683.  
  684.     public boolean keyDown(Event e, int k)
  685.     {
  686.     if (mode == TEXT) {
  687.         char c = (char)k;
  688.         img.setColor(col);
  689.         if (c >= 32 && c < 127) {
  690.             // Draw a character at the current position
  691.             String s = String.valueOf(c);
  692.             img.setFont(fn);
  693.             img.drawString(s, lx, ly);
  694.             lx += img.getFontMetrics().stringWidth(s);
  695.             }
  696.         blit();
  697.         }
  698.     return true;
  699.     }
  700.  
  701.     // flood
  702.     // Floodfill an array of pixels, using an iterative stack-based
  703.     // algorithm. BUG - the allocated stack is way too big.. what is
  704.     // a proper upper bound for flooding an w*h image?
  705.     void flood(int arr[], int w, int h, int x, int y, int b, int c)
  706.     {
  707.     int xstk[] = new int[w*h], ystk[] = new int[w*h];
  708.     int sp = 0;
  709.     xstk[sp] = x; ystk[sp] = y;
  710.     sp++;
  711.     while(sp != 0) {
  712.         // Remove the top pixel from the stack, and set it
  713.         sp--;
  714.         int px = xstk[sp], py = ystk[sp];
  715.         int pos = py*w + px;
  716.         arr[pos] = c;
  717.  
  718.         // Check surrounding pixels, and if OK add them to the stack
  719.         if (px > 0 && arr[pos-1] == b)
  720.             { xstk[sp] = px-1; ystk[sp] = py; sp++; }
  721.         if (py > 0 && arr[pos-w] == b)
  722.             { xstk[sp] = px; ystk[sp] = py-1; sp++; }
  723.         if (px < w-1 && arr[pos+1] == b)
  724.             { xstk[sp] = px+1; ystk[sp] = py; sp++; }
  725.         if (py < h-1 && arr[pos+w] == b)
  726.             { xstk[sp] = px; ystk[sp] = py+1; sp++; }
  727.         }
  728.     }
  729. }
  730.  
  731. // ControlPanel
  732. // A panel containing a vertical array of ImageButtons, each set at their
  733. // preferred sizes and positioned directly below each other.
  734. class ControlPanel extends Panel
  735. {
  736.     java.applet.Applet ap;        // applet for loading images
  737.     int w=0, h=0;
  738.  
  739.     ControlPanel(java.applet.Applet a)
  740.     {
  741.     ap = a;
  742.     setLayout(null);
  743.     }
  744.  
  745.     // addbutton
  746.     // Create and add an ImageButton from the given file
  747.     ImageButton addbutton(String file)
  748.     {
  749.     Image i = ap.getImage(ap.getCodeBase(),file);
  750.     ImageButton b = new ImageButton(i);
  751.     Dimension bs = b.preferredSize();
  752.     add(b);
  753.     b.reshape(0, h, bs.width, bs.height);
  754.     w = Math.max(w, bs.width);
  755.     h += bs.height;
  756.     return b;
  757.     }
  758.  
  759.     // addtoggle
  760.     // Create and add an ImageToggle from the given file
  761.     ImageToggle addtoggle(String file, ImageToggleGroup g, boolean s)
  762.     {
  763.     Image i = ap.getImage(ap.getCodeBase(),file);
  764.     ImageToggle t = new ImageToggle(i, s, g);
  765.     Dimension ts = t.preferredSize();
  766.     t.reshape(0, h, ts.width, ts.height);
  767.     add(t);
  768.     w = Math.max(w, ts.width);
  769.     h += ts.height;
  770.     return t;
  771.     }
  772.  
  773.     // addgap
  774.     // Add a p-pixel gap between buttons
  775.     void addgap(int p)
  776.     {
  777.     h += p;
  778.     }
  779.  
  780.     public Dimension preferredSize() { return new Dimension(w,h); }
  781.     public Dimension minimumSize() { return preferredSize(); }
  782. }
  783.  
  784.  
  785. // ImageSizeReq
  786. // A requestor that asks for the size of a new image.
  787. class ImageSizeReq extends FixedFrame
  788. {
  789.     PaintingCanvas canvas;
  790.     TextField width, height;
  791.     Button ok, can;
  792.  
  793.     ImageSizeReq(PaintingCanvas c)
  794.     {
  795.     super(new Dimension(200, 150));
  796.     canvas = c;
  797.     setLayout(new BorderLayout());
  798.     Panel top = new Panel();
  799.     top.setLayout(new GridLayout(2,2));
  800.     top.add(new Label("Width"));
  801.     top.add(width = new TextField("300"));
  802.     top.add(new Label("Height"));
  803.     top.add(height = new TextField("300"));
  804.     add("North",top);
  805.     Panel bot = new Panel();
  806.     bot.setLayout(new FlowLayout(FlowLayout.RIGHT));
  807.     bot.add(ok = new Button("Ok"));
  808.     bot.add(can = new Button("Cancel"));
  809.     add("South",bot);
  810.     setTitle("Image size");
  811.     pack();
  812.     show();
  813.     }
  814.  
  815.     public boolean handleEvent(Event e)
  816.     {
  817.     if (e.id == Event.WINDOW_DESTROY ||
  818.         e.id == Event.ACTION_EVENT && e.target == can)
  819.         dispose();
  820.     else if (e.id == Event.ACTION_EVENT && e.target == ok) {
  821.         // new width and height entered
  822.         int w = Integer.parseInt(width.getText());
  823.         int h = Integer.parseInt(height.getText());
  824.         if (w > 0 && h > 0)
  825.             canvas.setimage(createImage(w,h));
  826.         else
  827.             new ErrorWindow("Bogus dimensions");
  828.         dispose();
  829.         }
  830.     return super.handleEvent(e);
  831.     }
  832. }
  833.  
  834.